/**
 * @author Cator Vee  hi@catorv.com
 */
import { mixin, forEach, map, defined, isString, isObject } from './index'

const EXP_URL = /^(?:\.{1,2}|(?:\w+:)?\/)\//i
const URL_PASTER = document.createElement('a')

let cachedQuery

const location = window.location

export function urlEncode (str) {
  return encodeURIComponent(str)
}

export function urlDecode (str) {
  return /%u[0-9a-f]{4}/i.test(str) ? unescape(str) : decodeURIComponent(str)
}

export function buildUrl (query, path, host, protocol) {
  if (path && !/^\//.test(path)) {
    path = location.pathname.replace(/\/$/, '') + '/' + path
  }
  path = (path || location.pathname).replace(/^\//, '')
  query = isObject(query)
    ? queryString(query, path.indexOf('?') < 0 ? '?' : '&')
    : (query || location.search)
  path += query + location.hash
  if (EXP_URL.test(path)) {
    return path
  }
  return (protocol || location.protocol) + '//' + (host || location.host) + '/' + path
}

export function parseUrl (url) {
  URL_PASTER.href = url
  return map({
    protocol: '',
    host: '',
    port: '',
    pathname: '',
    search: '',
    hash: '',
    hostname: '',
    href: '',
    origin: ''
  }, function (_, name) {
    return URL_PASTER[name]
  })
}

export function queryString (parameters, prefix) {
  function _decode (str) {
    let result = {}
    if (str) {
      str.split('&').forEach(function (item) {
        let parts = item.split('=')
        let key = parts[0]
        let value = urlDecode(parts[1])
        if (/^[[{]/.test(value)) {
          try {
            value = JSON.parse(value)
          } catch (e) {
            // do nothing
          }
        }
        if (key in result) {
          if (Array.isArray(result[key])) {
            result[key].push(value)
          } else {
            result[key] = [result[key], value]
          }
        } else {
          result[key] = value
        }
      })
    }
    return result
  }

  if (!parameters || (isString(parameters) &&
      (parameters.indexOf('=') < 0 || parameters.charAt(0) === '?'))) {
    if (parameters && parameters.charAt(0) === '?') {
      return _decode(parameters.slice(1))
    }
    if (!cachedQuery) {
      cachedQuery = _decode(location.search.slice(1))
    }
    return parameters ? cachedQuery[parameters] : mixin({}, cachedQuery)
  } else if (isObject(parameters)) {
    prefix = prefix || ''
    let serialize = prefix
    forEach(parameters, function (value, key) {
      if (defined(value)) {
        if (serialize !== prefix) {
          serialize += '&'
        }

        if (Array.isArray(value)) {
          serialize += value.map(function (val) {
            if (isObject(val) || Array.isArray(val)) {
              val = JSON.stringify(val)
            }
            return key + '=' + urlEncode(val)
          }).join('&')
        } else {
          serialize += key + '=' +
            urlEncode(isObject(value) ? JSON.stringify(value) : value)
        }
      }
    })
    return (serialize === prefix ? '' : serialize)
  }

  return _decode(parameters)
}
